package org.destinyshine.cricket.connector.nio;

import org.apache.commons.lang3.ArrayUtils;
import org.destinyshine.cricket.connector.HttpServletRequestImpl;
import org.destinyshine.cricket.connector.ReuqestConsts;

import javax.servlet.http.HttpServletRequest;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Created by destinyliu on 2016/2/20.
 */
public class RequestParser implements ReuqestConsts {

    static final Charset ISO8859_1 = Charset.forName("ISO8859-1");

    public static final byte BYTE_SPACE = 32;

    /**
     * Carriage-Return
     */
    public static final byte CR = 13;

    /**
     * Line-Feeding
     */
    public static final byte LF = 10;

    private int currentIndex = 0;

    private int entityStartIndex = -1;

    /**
     * 头部，包括请求行
     */
    private byte[] headerBytes = new byte[(1024 * 16)];
    private int headerBytesSize = 0;
    /**
     * http实体
     */
    private ByteBuffer entityBytes;

    private boolean headerResolved = false;
    private int entityAllowed = ENTITY_ALLOWED_UNKNOWN;
    private boolean requestReady = false;
    private HttpServletRequestImpl request;

    public RequestParser() {
        //this.httpServletRequest = new HttpServletRequestImpl();
        //this.connectionReceiver = connectionReceiver;
        //this.connection = new Connection(new HttpServletRequestImpl(), new HttpServletResponseImpl());
    }

    /**
     * 新到达的byteBuffer,到达时已是可读状态。
     *
     * @param buffer
     */
    public void resolveByteBuffer(ByteBuffer buffer) {
        //判断头部是否超出限制
//        if (buffer.limit() + bytesSize > headerBytes.length) {
//            throw new IllegalArgumentException();
//        }
        //头部是否解析完了
        if (entityAllowed == ENTITY_ALLOWED_YES) {
            appendEntity(buffer);
        } else {

            byte[] bufferArray = buffer.array();
            int limit = buffer.limit();
            for (int i = 0, j = headerBytesSize; i < limit; i++, j++, headerBytesSize++) {
                headerBytes[j] = bufferArray[i];
                if (entityStartIndex == -1 && j > 4 && headerBytes[j - 3] == CR && headerBytes[j - 2] == LF && headerBytes[j - 1] == CR && headerBytes[j] == LF) {
                    entityStartIndex = j + 1;
                    break;
                }
            }

            if (entityStartIndex != -1) {
                buffer.position(entityStartIndex);
                parseRequestInfoAndHeaders();
                if(entityAllowed == ENTITY_ALLOWED_YES) {
                    appendEntity(buffer);
                }
            }
        }

        //
        if (entityBytes != null && !entityBytes.hasRemaining()) {
            //connectionReceiver.recevieConnection(connection);
            this.entityBytes.flip();
            this.request.setContentBytes(this.entityBytes);
            this.requestReady = true;
        }
    }

    /**
     * @return has entity.
     */
    private void parseRequestInfoAndHeaders() {
        String[] requestInfos = parseRequsetInfo();
        Map<String, List<String>> headers = parseHeaders();
        this.request = new HttpServletRequestImpl(requestInfos, headers);
        //connection.setRequest(request);
        //connection.setResponse(new HttpServletResponseImpl(this.channel));
        if (ArrayUtils.indexOf(new Object[]{"GET", "HEAD", "DELETE"}, request.getMethod()) != -1) {
            this.entityAllowed = ENTITY_ALLOWED_NO;
            this.requestReady = true;
            return;
        } else {
            this.entityAllowed = ENTITY_ALLOWED_YES;
            String conLenStr = request.getHeader("Content-Length");

            if (conLenStr != null) {

                int contentLength = Integer.parseInt(conLenStr);

                entityBytes = ByteBuffer.allocate(contentLength);
                //request.setContentBytes(entityBytes);
                //置空无用的bytes
                headerBytes = null;
            }
        }
        return;
    }

    /**
     * 首行，请求行
     *
     * @return 请求信息数组
     */
    public String[] parseRequsetInfo() {
        String[] requestInfos = new String[3];
        int reqi = 0;
        int mask = 0;
        while (true) {
            byte ch = headerBytes[currentIndex];
            if (ch == BYTE_SPACE) {
                requestInfos[reqi] = new String(headerBytes, mask, currentIndex - mask, ISO8859_1);
                reqi++;
                currentIndex++;
                mask = currentIndex;
            } else if (headerBytes[currentIndex] == CR && headerBytes[currentIndex + 1] == LF) {
                requestInfos[reqi] = new String(headerBytes, mask, currentIndex - mask, ISO8859_1);
                //换行
                currentIndex = currentIndex + 2;
                break;
            } else {
                currentIndex++;
            }
        }

        return requestInfos;
    }

    /**
     * 修复bug，使用：判断headerName的结束，但内容里可能会出现：，所以当换行的时候把headerName置空，当出现：时判断headerName是否为null.
     *
     * @return
     */
    public Map<String, List<String>> parseHeaders() {
        Map<String, List<String>> headers = new HashMap<String, List<String>>();
        int mark = currentIndex;
        String headerName = null;
        List<String> headerValues = null;
        //boolean shouldMask = false;
        while (true) {
            byte ch = headerBytes[currentIndex];
            if (ch == ':' && headerName == null) {
                //每行第一次出现冒号才是HeaderName。因为内容可能出现冒号。
                headerName = new String(headerBytes, mark, currentIndex - mark);
                headerValues = new LinkedList<String>();
                //headers.put(headerName, headerValues);
                mark = ++currentIndex;
            } else if (ch == ',') {
                String value = new String(headerBytes, mark, currentIndex - mark);
                headerValues.add(value);
                mark = ++currentIndex;
                continue;
            } else if (ch == ' ' && mark == currentIndex) {
                mark = ++currentIndex;
            } else if (ch == '\r' && headerBytes[currentIndex + 1] == '\n') {
                String value = new String(headerBytes, mark, currentIndex - mark);
                headerValues.add(value);
                headers.put(headerName, headerValues);
                headerName = null;//置空headerName，防止内容出现冒号，可以用headerName==null来判断是否新header.
                headerValues = null;
                mark = currentIndex = currentIndex + 2;
                if (headerBytes[currentIndex] == '\r' && headerBytes[currentIndex + 1] == '\n') {
                    currentIndex = currentIndex + 2;
                    break;
                }
            } else {
                currentIndex++;
            }

            //currentIndex++;
        }

        return headers;
    }

    private void appendEntity(ByteBuffer buffer) {
        entityBytes.put(buffer);
    }

    public boolean isRequestReady() {
        return requestReady;
    }

    public HttpServletRequest getRequest() {
        return request;
    }
}
